package com.fingard.dsp.bank.directbank.fngrd01.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Arrays;

/**
 * 按照保融数据处理逻辑包装的签名验签 API
 * @author Administrator
 *
 */
public class SignAPI {
	private static final String ENCODING = "gbk";
	/**
	 * 在 VERIFYSIGN_DETAIL_LENGTH 基础上增加下划线和状态信息
	 */
	private static final int SIGN_DETAIL_LENGTH = 32;
	/**
	 * 预估平均每行明细不会超过30个字符。
	 * 通常是 19 位的账号 + '_' + 9位以下的金额
	 */
	private static final int VERIFYSIGN_DETAIL_LENGTH = 30;
	/**
	 * RSA 帮助类
	 */
	private RSAHelper _Helper;
	public SignAPI(RSAHelper helper) throws Exception
	{
		if(helper == null)
			throw new Exception("参数不能为 null");
		_Helper = helper;
	}
	/**
	 * 获取要验签的字符串
	 * @param corpNum 企业编号
	 * @param batNo 批次号
	 * @param detailArray 明细内容数组，组成格式为 "账号_金额_交易状态"
	 * @return
	 */
	private String getString(String corpNum, String batNo, String[] detailArray, int avgDetailLength)
	{
		//对明细数据进行排序
		Arrays.sort(detailArray, new StringComparator());
		//创建 StringBuffer，给一初始值防止多次自动扩容造成额外内存占用
		StringBuffer buffer = new StringBuffer(corpNum.length() + batNo.length() + avgDetailLength * detailArray.length);
		buffer.append(corpNum).append("_").append(batNo);
		for(String detail : detailArray)
		{
			buffer.append(";").append(detail);
		}
		return buffer.toString();
	}
	/**
	 * 获取要验签的字符串
	 * @param corpNum 企业编号
	 * @param batNo 批次号
	 * @param detailArray 明细内容数组，组成格式为 "账号_金额_交易状态"
	 * @return
	 */
	String getToSignString(String corpNum, String batNo, String[] detailArray)
	{
		return getString(corpNum, batNo, detailArray, SIGN_DETAIL_LENGTH);
	}
	/**
	 * 获取要验签的字符串
	 * @param corpNum 企业编号
	 * @param batNo 批次号
	 * @param detailArray 明细内容数组，组成格式为 "账号_金额"
	 * @return
	 */
	String getToVerifySignString(String corpNum, String batNo, String[] detailArray)
	{
		return getString(corpNum, batNo, detailArray, VERIFYSIGN_DETAIL_LENGTH);
	}
	/**
	 * 根据待签名数据，生成签名值的 Base64 字符串
	 * @param corpNum
	 * @param batNo
	 * @param detailArray
	 * @return
	 * @throws SignatureException 
	 * @throws NoSuchAlgorithmException 
	 * @throws UnsupportedEncodingException 
	 * @throws InvalidKeyException 
	 */
	public String signToBase64(String corpNum, String batNo, String[] detailArray) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException
	{
		String sourceString = getToSignString(corpNum, batNo, detailArray);
		//MainClass.log("原始字符串=" + sourceString);
		return signToBase64(sourceString);
	}
	/**
	 * 对字符串进行签名，返回签名后的值的 Base64 值
	 * @param sourceString
	 * @return
	 * @throws InvalidKeyException
	 * @throws UnsupportedEncodingException
	 * @throws NoSuchAlgorithmException
	 * @throws SignatureException
	 */
	public String signToBase64(String sourceString) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException
	{
		return Base64Helper.encode(sign(sourceString));
	}
	/**
	 * 对字符串进行签名，返回签名后的值的  byte 数组
	 * @param sourceString
	 * @return
	 * @throws UnsupportedEncodingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws SignatureException 
	 * @throws InvalidKeyException 
	 */
	private byte[] sign(String sourceString) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, SignatureException
	{
		//GBK 编码
		byte[] sourceBytes = sourceString.getBytes(ENCODING);
		//md5 摘要
		String md5Hex = DigestHelper.getMD5HexString(sourceBytes);
		//MainClass.log("md5Hex=" + md5Hex);
		//GBK 编码
		byte[] toSignBytes = md5Hex.getBytes(ENCODING);
		//签名
		byte[] signBytes = _Helper.sign(toSignBytes);
		return signBytes;
	}
	
	/**
	 * 验签
	 * @param sourceString
	 * @param verifyCodeBase64
	 * @return
	 * @throws UnsupportedEncodingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws SignatureException 
	 * @throws InvalidKeyException 
	 */
	public boolean verifySign(String corpNum, String batNo, String[] detailArray, String verifyCodeBase64) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException
	{
		String sourceString = getToVerifySignString(corpNum, batNo, detailArray);
		return verifySign(sourceString, Base64Helper.decode(verifyCodeBase64));
	}
	/**
	 * 验签
	 * @param sourceString
	 * @param verifyCodeBase64
	 * @return
	 * @throws UnsupportedEncodingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws SignatureException 
	 * @throws InvalidKeyException 
	 */
	public boolean verifySign(String sourceString, String verifyCodeBase64) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException
	{
		return verifySign(sourceString, Base64Helper.decode(verifyCodeBase64));
	}
	/**
	 * 验签
	 * @param sourceString
	 * @param verifyCode
	 * @return
	 * @throws NoSuchAlgorithmException 
	 * @throws SignatureException 
	 * @throws InvalidKeyException 
	 * @throws UnsupportedEncodingException 
	 */
	private boolean verifySign(String sourceString, byte[] verifyCode) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException
	{
		//GBK 编码
		byte[] sourceBytes = sourceString.getBytes(ENCODING);
		//md5 摘要
		String md5Hex = DigestHelper.getMD5HexString(sourceBytes);
		//GBK 编码
		byte[] toSignBytes = md5Hex.getBytes(ENCODING);
		//验签
		return _Helper.verifySign(toSignBytes, verifyCode);
	}
}
